package me.pppploi8.webmcs.minecraft.server.core.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import me.pppploi8.webmcs.minecraft.server.core.McServerCore;
import me.pppploi8.webmcs.minecraft.server.struct.McMessageInterface;

/**
 * MinecraftServer开服核心类，负责最基本的开服和获取输入输出功能
 * @author pppploi8
 */
public class McServerCoreImpl implements McServerCore {
    private File serverPath = null;
    private File jarFile = null;
    private String javaPath = null;
    private String javaParam = null;
    private String startParam = null;
    private String maxMemory = null;
    private Process process = null;
    private long timeout = 300;

    private InputStream sis = null;//控制台标准输出流
    private InputStream eis = null;//控制台错误输出流
    private OutputStream sos = null;//控制台输入流
    private List<McMessageInterface> messages = new ArrayList<>();

    private boolean isRun = false;  //MC服务器是否启动
 
    class McInputStreamThread extends Thread {
        public void run(){
            try {
                byte[] bytes = new byte[1024];
                int i;
                while ((i = sis.read(bytes)) != -1){
                    for (McMessageInterface mm : messages) {
                        mm.message(new String(bytes,0,i,"UTF-8"));
                    }
                }
            } catch (Exception ignored) { }
        }
    }
    class McErrorStreamThread extends Thread {
        public void run(){
            try {
                byte[] bytes = new byte[1024];
                int i;
                while ((i = eis.read(bytes)) != -1){
                    for (McMessageInterface mm : messages) {
                        mm.message(new String(bytes,0,i,"UTF-8"));
                    }
                }
            } catch (Exception ignored) { }
        }
    }
    /**
     * 设置服务器重启超时时间，如果超过这个时间服务器仍未关闭，则强制关闭服务器
     * @param timeout 单位为秒
     * @return 返回对象自身
     */
    @Override
	public McServerCore setRestartTimeOut(long timeout){
        this.timeout = timeout;
        return this;
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#getJarFile()
	 */
    @Override
	public File getJarFile(){
        return jarFile;
    }
    /**
     * 设置java路径，用于启动服务端，如果不填写默认使用java
     * @param javaPath java路径
     * @return 返回独享自身
     */
    @Override
	public McServerCore setJavaPath(String javaPath){
        this.javaPath = javaPath;
        return this;
    }
    /**
     * 设置jar文件路径，如果不设置默认为运行目录下server\start.jar
     * @param filePath jar文件路径
     * @return 返回对象自身
     */
    @Override
	public McServerCore setJarFile(String filePath){
        this.jarFile = new File(serverPath.getPath() + "/" + filePath);
        return this;
    }
    @Override
	public McServerCore setStartParam(String startParam) {
        this.startParam = startParam;
        return this;
    }
    @Override
	public McServerCore setMaxMemory(String maxMemory) {
        this.maxMemory = maxMemory;
        return this;
    }
    /**
     * 设置服务端运行目录
     * @param path 如果不设置默认为运行目录下的server
     * @return 
     */
    @Override
	public McServerCore setServerPath(String path){
        this.serverPath = new File(path);
        return this;
    }
    /**
     * 设置一个回调用来接受服务端发送的消息
     * @param mm 回调接受接口
     * @return 返回对象自身
     */
    @Override
	public McServerCore setMessageNotice(McMessageInterface mm){
        messages.add(mm);
        return this;
    }
    /**
     * 设置自定义java启动命令行
     * @param javaParam java启动参数，会被加入到 java之后，-jar 'jar服务端文件'之前的位置
     * @return 返回对象自身
     */
    @Override
	public McServerCore setJavaParam(String javaParam){
        this.javaParam = javaParam;
        return this;
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#start()
	 */
    @Override
	public boolean start(){
        if (process != null && process.isAlive()){
            return false;
        }
        if (serverPath==null || !serverPath.exists() || !serverPath.isDirectory())
            return false;
        if (jarFile==null || !jarFile.exists() || !jarFile.isFile())
            return false;
        try{
	        File eulaFile = new File(serverPath + "/eula.txt");
	        if (!eulaFile.exists())
	            eulaFile.createNewFile();
	        OutputStream os = new FileOutputStream(eulaFile);
	        os.write("eula=true".getBytes());
	        os.flush();
	        os.close();
        }catch(Exception e){}//eula创建相关异常无视
        javaParam = javaParam==null?"":javaParam;
        startParam = startParam==null?"":startParam;
        String memory = "";
        try {
            int m = Integer.parseInt(maxMemory);
            if (m > 0){
                memory = " -Xms" + m + "m -Xmx" + m +"m";
            }
        } catch (Exception e) { }
        String commond = null;
        if (javaPath == null || javaPath.equals("")){
        	commond = "java" + memory  + " " + javaParam + " -jar -Dfile.encoding=utf-8 -Djline.terminal=jline.UnsupportedTerminal \"" + jarFile.getAbsolutePath() + "\" nogui" + startParam;
        } else {
        	commond = "\"" + javaPath + "\"" + memory + " " + javaParam + " -jar -Dfile.encoding=utf-8 -Djline.terminal=jline.UnsupportedTerminal \"" + jarFile.getAbsolutePath() + "\" nogui" + startParam;
        }
        try {
	        if(!System.getProperties().getProperty("os.name").toLowerCase().startsWith("windows")){
	        	process = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",commond},null,serverPath);
	        }else{
	        	process = Runtime.getRuntime().exec(commond,null,serverPath);
	        }
            sis = process.getInputStream();
            eis = process.getErrorStream();
            sos = process.getOutputStream();
            new McInputStreamThread().start();
            new McErrorStreamThread().start();
            isRun = true;
            new Thread(new Runnable(){
                @Override
                public void run(){
                    // 进程监控线程，如果异常退出则自动重启，直到按下关闭服务器按钮为止
                    if (process != null) {
                        try{process.waitFor();}catch(Exception e){}
                    }
                    if (isRun){
                        for (McMessageInterface mm : messages) {
                            mm.message("服务器进程已退出，退出代码：" + process.exitValue() + "，系统即将自动重启服务器进程...\n");
                        }
                        start(); // 递归启动
                    }
                }
            }).start();
            return true;
        } catch (Exception e) {
        	e.printStackTrace();
            return false;
        }
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#sendMessage(java.lang.String)
	 */
    @Override
	public boolean sendMessage(String message){
    	if (!getServerStatus())
    		return false;
        try {
            sos.write((message+"\n").getBytes("UTF-8"));
            sos.flush();
            return true;
        } catch (Exception e) {
        	e.printStackTrace();
            return false;
        }
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#getServerPath()
	 */
    @Override
	public File getServerPath(){
    	return serverPath;
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#getServerStatus()
	 */
    @Override
	public boolean getServerStatus(){
        return isRun || (process!=null && process.isAlive());// process==null?false:process.isAlive();
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#stop()
	 */
    @Override
	public boolean stop(){
	    if (sendMessage("stop")){
            isRun = false;
            return true;
	    }
        return false;
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#termination()
	 */
    @Override
	public boolean termination(){
        isRun = false;
        if (process == null)
            return false;
        process.destroy();
        process = null;
        return true;
    }
    /* (non-Javadoc)
	 * @see me.pppploi8.webmcs.minecraft.server.core.McServerCore#restart()
	 */
    @Override
	public boolean restart(){
        if (!getServerStatus())
            return false;
        sendMessage("stop");
        timeout = timeout<0?300:timeout;
        try {
            process.waitFor(timeout,TimeUnit.SECONDS);
        } catch (Exception e) { }
        if (getServerStatus())
            return false;
        start();
        return true;
    }
}
